{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# How Gradients Are Calculated?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```{warning}\n",
    "This part may be more mathematics focused. If you simply want to grasp the intuition behind deep learning, feel free to skip the section.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```{note}\n",
    "This explanation will focus on how PyTorch calculates gradients. Recently TensorFlow has switched to the same model so the method seems pretty good.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Chain rule\n",
    "\n",
    "$$\n",
    "\\frac{d f}{d x} = \\frac{d f}{d y} \\frac{d y}{d x}\n",
    "$$\n",
    "\n",
    "Chain rule is basically a way to calculate derivatives for functions that are very composed and complicated. With chain rule at hand, we will be able to take derivatives for functions that look familiar but complicated."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example usage of chain rule\n",
    "\n",
    "It's very easy to calculate the derivative of $ 5 x $, which is $ 5 $. It's also obvious that the derivative of $ x^3 $ is $ 3 x^2 $. However, what's the gradient of $ (5 x)^3 $? \n",
    "\n",
    "It's easy, you say. $ (5 x)^3 = 125 x^3 $, so the derivative is $ 125 (3 x^2) = 375 x^2 $. You're right. For demonstration purpose, let's see how chain rule can derive the same answer.\n",
    "\n",
    "\n",
    "First, let $ y = 5 x $. Then, the chain rule:\n",
    "\n",
    "$$\n",
    "\\frac{d f}{d x} = \\frac{d f}{d y} \\frac{d y}{d x}\n",
    "$$\n",
    "\n",
    "will reduce to:\n",
    "\n",
    "$$\n",
    "\\frac{d (5 x)^3}{d x} = \\frac{d (5 x)^3}{d (5 x)} \\frac{d (5 x)}{d x}\n",
    "$$\n",
    "\n",
    "We know that $ \\frac{d x^3}{x} = 3 x^2 $.\n",
    "\n",
    "So that:\n",
    "$$\n",
    "\\frac{d (5 x)^3}{d (5 x)} \\frac{d (5 x)}{d x} = 3 (5 x)^2 5 = 375 x^2\n",
    "$$\n",
    "\n",
    "The same answer. So why chain rule? Imagine if it's not $ (5 x)^3 $, but $ \\cos(\\sin (5 x) 8 x) $? This is where chain rule comes in handy. It can decompose the complicated step by step an arrive at the solution that may be otherwise too complicated. I'm not going to derive the derivatives of $ \\cos(\\sin (5 x) 8 x) $. Try it yourself!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## So how does PyTorch calculate gradients?\n",
    "\n",
    "Gradients are multi-dimensional derivatives. A gradient for a list of parameter $ X $ with regards to the number $ y $ can be defined as:\n",
    "\n",
    "$$\n",
    "\\begin{bmatrix}\n",
    "\\frac{d y}{d x_1} \\\\\n",
    "\\frac{d y}{d x_2} \\\\\n",
    "\\vdots \\\\\n",
    "\\frac{d y}{d x_n}\n",
    "\\end{bmatrix}\n",
    "$$\n",
    "\n",
    "Gradients are calculated using chain rule. Turns out that most functions you use, that PyTorch provides, are composed by easy functions, and PyTorch derives the gradients for the "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
